﻿using Google.OrTools.ConstraintSolver;
using Study.GoogleOptimizationTools.ConsoleApp.Extensions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Study.GoogleOptimizationTools.ConsoleApp.Examples
{
    public class ExampleCommonTaskSolver
    {

        /// <summary>
        /// 设置限时，解数目等
        /// </summary>
        static public void Example_1()
        {
            var source = Enumerable.Range(1, 100).ToArray();
            var target = 200;

            //定义求解器
            var solver = new Solver("Example_1");

            var x = solver.MakeIntVarArray(source.Length, 0, 1);
            var total = source.Zip(x, (f, s) => f * s).Aggregate((f, s) => f + s);
            solver.Add(total == target);

            var db = solver.MakePhase(x, Solver.CHOOSE_FIRST_UNBOUND, Solver.ASSIGN_MIN_VALUE);


            //定义收集器，用于保存解的。
            var sc = solver.MakeAllSolutionCollector();
            sc.Add(x);//往收集器添加变量，他才知道需要收集的是哪些

            //定义限时
            int limitSecodes = 5;
            long limitMilliseconds = Convert.ToInt64(TimeSpan.FromSeconds(limitSecodes).TotalMilliseconds);
            SearchLimit limitTime = solver.MakeTimeLimit(limitMilliseconds);

            //定义限制解的数目
            int limitSolutionCount = 5;
            SearchLimit limitCount = solver.MakeSolutionsLimit(limitSolutionCount);

            //var vtb = new VoidToBoolean();
            
            //solver.MakeCustomLimit()

            //执行求解的时候，传入限时和收集器
            //假如没有得到解(超时或没有解)，则返回fasle
            //目前理解为：每个SolutionCollector中保存了一个解决方案的变量赋值器
            //因此当调用Value()方法时，内部会调用赋值器，从而得到解
            if (solver.Solve(db, limitTime, limitCount, sc))
            {
                string split = new string('=', 10);

                //通过收集器即可获取到结果
                Console.WriteLine($"结果数目:{sc.SolutionCount()}");

                foreach (var i in Enumerable.Range(0, sc.SolutionCount()))
                {
                    Console.WriteLine(split);

                    //sc.Value(i, f)  收集器中，第i个方案中，f变量的值，作为Value()方法的返回值
                    var query = from n in ZipExtentions.Zip(x, source)
                                let xv = sc.Value(i, n.First)
                                where xv > 0
                                select  new
                                {
                                    count = xv,
                                    number = n.Second
                                };


                    foreach (var item in query)
                    {
                        Console.WriteLine($"{item.number} * {item.count} = {item.number * item.count}");
                    }
                }
            }
            else
            {
                Console.WriteLine("无解");

            }

            if (limitTime.IsCrossed())
            {
                //计算器被触发
                Console.WriteLine("超过了设定的限时");
            }
            else
            {
                Console.WriteLine("没有超时");
            }
        }


        /// <summary>
        /// 检查模型
        /// </summary>
        static public void Example_2()
        {

            var solver = new Solver("Example_1");

            var x = solver.MakeIntVar(0, 10);
            Constraint c = solver.MakeEquality(x, 10);

            //Constraint的ToString()方法可显示约束关系字符串。用于查看模型的约束条件是否正确。
            Console.WriteLine(c.ToString());


        }

        /// <summary>
        /// 验证模型的性能
        /// </summary>
        static public void Example_3()
        {

            var solver = new Solver("Example_1");

            var x = solver.MakeIntVar(0, 10);
            Constraint c = solver.MakeEquality(x, 10);


            //solver内部有一计时器，这个可以获取计时器的时间。单位毫秒
            //可用于显示模型创建的时间
            //只要在调用Solver方法之前记录时间，在执行完毕后再次调用WallTime获取时间，两者相减即可得到求解时间
            Console.WriteLine(solver.WallTime());

            //搜索树中，子叶的数量。
            //这个在求解后获取，可以用于评估模型的性能，叶子数越小，表示模型回溯次数越小
            Console.WriteLine(solver.Failures());

            //搜索树的分支数量。
            //分支越少，表示模型找到最优解的路径越短。
            Console.WriteLine(solver.Branches());


        }


        class CancelLimit : VoidToBoolean
        {
            public CancellationToken CancellationToken { get; set; }


            public override bool Run()
            {
                return this.CancellationToken.IsCancellationRequested;
            }

            
        }

        static public void Example_4(CancellationToken cancellationToken)
        {
            var source = Enumerable.Range(1, 100).ToArray();
            var target = 200;

            //定义求解器
            var solver = new Solver("Example_1");

            var x = solver.MakeIntVarArray(source.Length, 0, 1);
            var total = source.Zip(x, (f, s) => f * s).Aggregate((f, s) => f + s);
            solver.Add(total == target);

            var db = solver.MakePhase(x, Solver.CHOOSE_FIRST_UNBOUND, Solver.ASSIGN_MIN_VALUE);


            //定义收集器，用于保存解的。
            var sc = solver.MakeAllSolutionCollector();
            sc.Add(x);//往收集器添加变量，他才知道需要收集的是哪些


            //让外部可主动取消
            //取消后，有可能已经得到一定数量的解。
            SearchLimit myLimit = solver.MakeCustomLimit(new CancelLimit { CancellationToken=cancellationToken});


            //
            if (solver.Solve(db, myLimit, sc))
            {
                string split = new string('=', 10);

                //通过收集器即可获取到结果
                Console.WriteLine($"结果数目:{sc.SolutionCount()}");

                foreach (var i in Enumerable.Range(0, sc.SolutionCount()))
                {
                    Console.WriteLine(split);

                    //sc.Value(i, f)  收集器中，第i个方案中，f变量的值，作为Value()方法的返回值
                    var query = from n in ZipExtentions.Zip(x, source)
                                let xv = sc.Value(i, n.First)
                                where xv > 0
                                select new
                                {
                                    count = xv,
                                    number = n.Second
                                };


                    foreach (var item in query)
                    {
                        Console.WriteLine($"{item.number} * {item.count} = {item.number * item.count}");
                    }
                }
            }
            else
            {
                Console.WriteLine("无解");

            }

            if (myLimit.IsCrossed())
            {
                //计算器被触发
                Console.WriteLine("触发了取消");
            }
            else
            {
                Console.WriteLine("没有触发取消");
            }
        }
    }
}
